#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>
#include <sys/epoll.h>

#include "Channel.hpp"
#include "../Log.hpp"

static const int MAX_EPOLLEVENTS = 1024;

class Epoller
{
private:
    int _epfd;
    struct epoll_event _evs[MAX_EPOLLEVENTS];
    std::unordered_map<int, Channel *> _channels;

private:
    // 判断一个Channel是否已经在epoll的监控之内
    bool HasChannel(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it != _channels.end())
            return false;
        return true;
    }

    // 更新事件
    void Update(Channel *channel, int op)
    {
        struct epoll_event ev;
        ev.data.fd = channel->Fd();
        ev.events = channel->Events();
        int ret = epoll_ctl(_epfd, op, channel->Fd(), &ev);
        if (ret < 0)
        {
            ERR_LOG("epoll update fail");
        }
    }

public:
    Epoller()
    {
        _epfd = epoll_create1(10);
        if (_epfd < 0)
        {
            ERR_LOG("epoller create fail");
            abort();
        }
    }
    ~Epoller() = default;

    // 更新事件监控
    void UpdateEvent(Channel *channel)
    {
        // Channel在监控范围之内则修改，不在则添加
        bool ret = HasChannel(channel);
        if (ret == false)
        {
            _channels.insert(std::make_pair(channel->Fd(), channel));
            Update(channel, EPOLL_CTL_ADD);
        }
        else
        {
            Update(channel, EPOLL_CTL_MOD);
        }
    }
    // 移除事件监控
    void RemoveEvent(Channel *channel)
    {
        // 在哈希map找到该Channel并移除
        auto it = _channels.find(channel->Fd());
        if (it == _channels.end())
            return;
        _channels.erase(it);
        // 取消epoll对该Channel的事件监控
        Update(channel, EPOLL_CTL_DEL);
    }

    // 启动事件监控
    void StartEpoll(std::vector<Channel *> *active)
    {
        int nfds = epoll_wait(_epfd, _evs, MAX_EPOLLEVENTS, -1);
        if (nfds < 0)
        {
            // 被信号打断了
            if (errno == EINTR)
                return;
            ERR_LOG("epoll wait error:%s", strerror(errno));
            abort();
        }
        // 将nfds个就绪的Channel放入到vector中
        for (int i = 0; i < nfds; i++)
        {
            // 找到对应的Channel
            auto it = _channels.find(_evs[i].data.fd);
            // 更新Channel内部的记录就绪事件变量,因为现在还没有处理事件，所以必须要把就绪事件给标记下来
            it->second->SetREvents(_evs[i].events);
            // 放入到vector中
            active->push_back(it->second);
        }
    }
};